/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef IMAGE_KNIFE_C_KEKE_NAPI_PARSER_H
#define IMAGE_KNIFE_C_KEKE_NAPI_PARSER_H

#include <string>
#include "napi/native_api.h"
#include "log.h"
#include "include/imageknife_type.h"
#include "image_source.h"

namespace ImageKnifePro {

class NapiParser {
public:
    static std::string ParseString(napi_env env, napi_value value)
    {
        std::string result;
        napi_valuetype type;
        napi_typeof(env, value, &type);
        if (type == napi_undefined) {
            // 等价于空字符串
            return result;
        }

        size_t length = 0;
        napi_status status = napi_get_value_string_utf8(env, value, nullptr, 0, &length);
        if (status != napi_ok) {
            IMAGE_KNIFE_LOG(LOG_ERROR, "Parse Napi String Get length failed, napi status: %{public}d", status);
            return result;
        }

        // Js层可能传入非常大的base64图片字符串，因此不使用栈内存接收
        result.resize(length);
        status = napi_get_value_string_utf8(env, value, &result[0], length + 1, nullptr);
        if (status != napi_ok) {
            IMAGE_KNIFE_LOG(LOG_ERROR, "Parse Napi String failed, napi status: %{public}d", status);
        }
        return result;
    }

    static CacheStrategy ParseCacheStrategy(napi_env env, napi_value value)
    {
        int cacheStrategy = 0;
        napi_get_value_int32(env, value, &cacheStrategy);
        return static_cast<CacheStrategy>(cacheStrategy);
    }

    static void ParseImageSource(ImageSource &imageSource, napi_env env, napi_value value)
    {
        napi_valuetype type;
        napi_status status = napi_typeof(env, value, &type);
    
        if (type == napi_string) {
            imageSource.SetString(ParseString(env, value));
        } else if (type == napi_number) {
            int num;
            status = napi_get_value_int32(env, value, &num);
            imageSource.SetNumber(num);
        } else if (type == napi_object) {
            // 通过成员属性名判断是否为pixelMap
            bool hasProperty = false;
            status = napi_has_named_property(env, value, "createAlphaPixelmap", &hasProperty);
            if (status == napi_ok && hasProperty) {
                OH_PixelmapNative *pixelmap;
                if (OH_PixelmapNative_ConvertPixelmapNativeFromNapi(env, value, &pixelmap) == IMAGE_SUCCESS) {
                    imageSource.SetPixelMap(pixelmap);
                }
                return;
            }
            ParseImageSourceResource(imageSource, env, value);
        }
    }

    static float ParseFloat(napi_env env, napi_value value)
    {
        double doubleValue = 0;
        napi_valuetype type;
        napi_typeof(env, value, &type);
        if (type == napi_number) {
            napi_get_value_double(env, value, &doubleValue);
            // 防止应用层非法输入导致的浮点溢出
            if (std::isnan(doubleValue)) {
                IMAGE_KNIFE_LOG(LOG_ERROR, "NapiParse Float Failed: Nan");
                return 0;
            }
        } else {
            IMAGE_KNIFE_LOG(LOG_ERROR, "NapiParse Float Failed: not napi_number, type:%{public}d", type);
        }

        return doubleValue;
    }

    static int ParseInt(napi_env env, napi_value value)
    {
        int num = 0;
        napi_valuetype type;
        napi_typeof(env, value, &type);
        if (type == napi_number) {
            napi_get_value_int32(env, value, &num);
        } else {
            IMAGE_KNIFE_LOG(LOG_ERROR, "NapiParse Int Failed: not napi_number, type:%{public}d", type);
        }
        return num;
    }

    static uint32_t ParseUint32(napi_env env, napi_value value)
    {
        uint32_t num = 0;
        napi_valuetype type;
        napi_typeof(env, value, &type);
        if (type == napi_number) {
            napi_get_value_uint32(env, value, &num);
        } else {
            IMAGE_KNIFE_LOG(LOG_ERROR, "NapiParse Uint Failed: not napi_number, type:%{public}d", type);
        }
        return num;
    }

    // 如果应用层传入的是非法值，则返回设置的默认的值
    static bool ParseBoolean(napi_env env, napi_value value, const bool defaultValue)
    {
        bool boolean = defaultValue;
        napi_valuetype type;
        napi_typeof(env, value, &type);
        if (type == napi_boolean) {
            if (napi_get_value_bool(env, value, &boolean) != napi_ok) {
                IMAGE_KNIFE_LOG(LOG_ERROR, "NapiParse Boolean Failed");
                return defaultValue;
            }
        } else {
            IMAGE_KNIFE_LOG(LOG_ERROR, "NapiParse Boolean Failed: not napi_boolean, type:%{public}d", type);
        }
        return boolean;
    }

    static napi_value ParseProperty(napi_env env, napi_value object, const char *propertyName)
    {
        IMAGE_KNIFE_CHECK_RETURN(object == nullptr, nullptr);
        bool isExist = false;
        napi_has_named_property(env, object, propertyName, &isExist);
        if (isExist) {
            napi_value value;
            if (napi_get_named_property(env, object, propertyName, &value) == napi_ok) {
                napi_valuetype type;
                napi_typeof(env, value, &type);
                if (type != napi_undefined) {
                    return value;
                }
            }
        }
        return nullptr;
    }
private:
    static void ParseImageSourceResource(ImageSource &imageSource, napi_env env, napi_value value)
    {
        // 通过成员属性名判断是否为Resource
        Resource resource;
        napi_value id = ParseProperty(env, value, "id");
        if (id == nullptr) {
            return;
        } else {
            napi_get_value_int32(env, id, &resource.id);
        }

        napi_value bundleName = ParseProperty(env, value, "bundleName");
        if (bundleName != nullptr) {
            resource.bundleName = ParseString(env, bundleName);
        }

        napi_value moduleName = ParseProperty(env, value, "moduleName");
        if (moduleName != nullptr) {
            resource.moduleName = ParseString(env, moduleName);
        }

        napi_value params = ParseProperty(env, value, "params");
        if (params != nullptr) {
            uint32_t arrLength = 0;
            napi_get_array_length(env, params, &arrLength);
            if (arrLength > 0) {
                napi_value element;
                // 这里参考imageknife ArkTs的逻辑，取第一个
                napi_get_element(env, params, 0, &element);
                resource.param = ParseString(env, element);
            }
        }
        imageSource.SetResource(resource);
    }
};

}

#endif //IMAGE_KNIFE_C_KEKE_NAPI_PARSER_H
